/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.operation;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Random;

import cgl.granules.exception.CommunicationsException;
import cgl.granules.exception.DeploymentException;
import cgl.granules.exception.MarshallingException;
import cgl.granules.scheduler.TaskLifecycleMetrics;

/**
 * This is a class that encapsulates several of the functionality that need to
 * be supported by Granules application deployers. By extending this class,
 * application-specific deployers are easier to develop.
 * 
 * @author Shrideep Pallickara
 * 
 */
public class InstanceDeployer implements JobLifecycleObserver {
    private long startTime = 0;
    private int startCount = 0;
    private int completionCount = 0;
    private int jobberId = 0;
    private JobSubmission jobber;
    private String moduleName = "InstanceDeployer: ";


    /**
     * Resets the start timer which tracks the time at which an application
     * began execution.
     */
    public void setStartTime() {
        startTime = System.currentTimeMillis();
    }


    /**
     * Resets the task completion counts maintained by this class. These counts
     * are updated as the application progresses in its distributed execution.
     */
    public void resetTrackers() {
        startCount = 0;
        completionCount = 0;
    }


    /**
     * Create a Job Identifier.
     * 
     * @return A UUID representing the Job identifier that was created.
     */
    public String createJobIdentifier() {
        return jobber.createJobIdentifier();
    }


    /**
     * This initializes the deployer based on the specified argument. This
     * includes initializing communications based on the specified parameters
     * and also discovering resources.
     * 
     * @param streamingProperties
     *            The properties for streaming
     * 
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws IOException
     *             If there are IO problems.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     * @throws DeploymentException
     *             If no resources are available.
     */
    public void initialize(Properties streamingProperties)
        throws CommunicationsException, IOException, MarshallingException,
        DeploymentException {
        initialize(streamingProperties, "niotcp");
    }


    /**
     * This initializes the deployer based on the specified arguments. This
     * includes initializing communications based on the specified parameters
     * and also discovering resources.
     * 
     * @param streamingProperties
     *            The properties for streaming
     * @param commType
     *            The communications type
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws IOException
     *             If there are IO problems.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     * @throws DeploymentException
     *             If no resources are available.
     */
    public void initialize(Properties streamingProperties, String commType)
        throws CommunicationsException, IOException, MarshallingException,
        DeploymentException {
        if (jobberId == 0) {
            Random random = new Random();
            jobberId = random.nextInt();
        }
        initialize(null, jobberId, streamingProperties, commType);
    }


    /**
     * This initializes the deployer based on the specified arguments. This
     * includes initializing communications based on the specified parameters
     * and also discovering resources.
     * 
     * @param configurationFile
     *            The configuration file
     * @param entityId
     *            The ID for the entity
     * @param props
     *            The properties for streaming
     * @param commType
     *            The communications type
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws IOException
     *             If there are IO problems.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     * @throws DeploymentException
     *             If no resources are available.
     */
    public void initialize(String configurationFile, int entityId,
                           Properties props, String commType)
        throws CommunicationsException, IOException, MarshallingException,
        DeploymentException {
        jobber = JobSubmission.getInstance();
        jobber.initialize(configurationFile, entityId, props, commType);

        jobber.discoverResource();
        System.out.println("\n(" + jobber.getNumberOfAvailableResources()
            + ") resources are available for use.");
    }


    /**
     * This method deploys a set of operations on a specified number of
     * resources.
     * 
     * @param operations
     *            The set of operations that need to be deployed.
     * @return The JobLifecycle object that allows one to track the status of
     *         the individual tasks that comprise the Job
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws DeploymentException
     *             If there are deployment related exceptions that are reported.
     *             One reason why this might happen is if the number of
     *             requested resources exceeds the number of available
     *             resources.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     */
    public ProgressTracker deployOperations(Operation[] operations)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        String jobIdentifier = jobber.createJobIdentifier();
        return deployOperations(operations, jobIdentifier);
    }


    /**
     * This method deploys a set of operations on the set of available
     * resources. All operations will be part of the Job identified by the
     * <code>jobIdentifier</code> argument.
     * 
     * @param operations
     *            The set of operations that need to be deployed.
     * 
     * @param jobIdentifier
     *            The identifier that groups these operations together.
     * @return The JobLifecycle object that allows one to track the status of
     *         the individual tasks that comprise the Job
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws DeploymentException
     *             If there are deployment related exceptions that are reported.
     *             One reason why this might happen is if the number of
     *             requested resources exceeds the number of available
     *             resources.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     */
    public ProgressTracker deployOperations(Operation[] operations,
                                            String jobIdentifier)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        int numOfResources = jobber.getNumberOfAvailableResources();
        return deployOperations(operations, numOfResources, jobIdentifier);
    }


    /**
     * This method deploys a set of operations on a specified number of
     * resources. All operations will be part of the Job identified by the
     * <code>jobIdentifier</code> argument.
     * 
     * @param operations
     *            The set of operations that need to be deployed.
     * @param numOfResources
     *            The number of resources on which this needs to be deployed.
     * @param jobIdentifier
     *            The identifier that groups these operations together.
     * @return The JobLifecycle object that allows one to track the status of
     *         the individual tasks that comprise the Job
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws DeploymentException
     *             If there are deployment related exceptions that are reported.
     *             One reason why this might happen is if the number of
     *             requested resources exceeds the number of available
     *             resources.
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     */
    public ProgressTracker deployOperations(Operation[] operations,
                                            int numOfResources,
                                            String jobIdentifier)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        return jobber.deploy(operations, numOfResources, jobIdentifier, this);
    }


    /**
     * Returns the status of all Jobs that have been submitted from this node in
     * question.
     */
    public String getStatus() {
        return jobber.getStatus();
    }


    /**
     * This method refreshes the status of various tasks that comprise a Job.
     * DIstributed resources will be contacted to retrieve the most up-to-date
     * status of the tasks.
     * 
     * @param jobIdentifier
     *            The ID of the Job that needs to refreshed.
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws DeploymentException
     *             If there are deployment related exceptions that are reported
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     */
    public void refreshStatus(String jobIdentifier)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        ProgressTracker tracker = jobber.getStatusOfJob(jobIdentifier);
        if (tracker == null) {
            throw new DeploymentException("Invalid JobID (" + jobIdentifier
                + ") specified.");
        }
        Enumeration<String> taskIdentifiers = tracker.getTaskIdentifiers();
        while (taskIdentifiers.hasMoreElements()) {
            String taskIdentifier = taskIdentifiers.nextElement();
            updateTaskStatus(jobIdentifier, taskIdentifier);
        }

    }


    /* Initiate status update for a specific task within the Job in question */
    private void updateTaskStatus(String jobIdentifier, String taskIdentifier)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        jobber.getStatusOfTask(jobIdentifier, taskIdentifier);
    }


    /**
     * This method refreshes the status of a specific task that is part of the
     * Job in question. Distributed resources will be contacted to retrieve the
     * most up-to-date status of this task.
     * 
     * @param jobIdentifier
     *            The ID of the Job that needs to refreshed.
     * @param taskIdentifier
     *            The ID of the Task that needs to refreshed.
     * @throws CommunicationsException
     *             If there are problems during network communications.
     * @throws DeploymentException
     *             If there are deployment related exceptions that are reported
     * @throws MarshallingException
     *             If there are problems marshalling the requests.
     */
    public void refreshStatus(String jobIdentifier, String taskIdentifier)
        throws CommunicationsException, DeploymentException,
        MarshallingException {
        ProgressTracker tracker = jobber.getStatusOfJob(jobIdentifier);
        if (tracker == null) {
            throw new DeploymentException("Invalid JobID (" + jobIdentifier
                + ") specified.");
        }
        updateTaskStatus(jobIdentifier, taskIdentifier);
    }


    @Override
    public void completedExecution(String jobIdentifier, String taskIdentifier,
                                   TaskLifecycleMetrics taskLifecycleMetrics) {
        String stringRep =
            "\nCompleted execution of task [" + taskIdentifier + "] ("
                + completionCount + ") ";

        /*
         * "that is part of the job (" + jobIdentifier +
         * "). The metrics associated with the execution are :\n" +
         * taskLifecycleMetrics + "\n";
         */
        System.out.println(stringRep);
        completionCount++;

    }


    @Override
    public void completedSuccessfully(String jobIdentifier,
                                      ProgressTracker progressTracker) {
        long completionOverhead = System.currentTimeMillis() - startTime;
        System.out.println("\nCompleted the SUCCESSFUL execution of the job ("
            + jobIdentifier + ") in [" + completionOverhead / 1000
            + "] seconds \n");

    }


    @Override
    public void completedWithErrors(String jobIdentifier,
                                    ProgressTracker progressTracker) {
        System.out.println("Completed execution of job (" + jobIdentifier
            + ") with errors!");
    }


    @Override
    public void startedExecution(String jobIdentifier, String taskIdentifier) {
        String stringRep =
            "Started execution of task [" + taskIdentifier + "] ( "
                + startCount + ")";
        // "that is part of the job (" + jobIdentifier + ")";
        System.out.println(stringRep);
        startCount++;

    }

}
